<template>
  <div class="map-container">
    <div id="map"></div>
    <add-icon class="center-cursor" style="color: red" size="2rem"></add-icon>
    <div class="draw-point-container" v-if="draw_tool">
      <t-button
        theme="primary"
        size="large"
        :icon="addIcon"
        shape="circle"
        aria-label="画点"
        class="draw-point-add"
        @click="onDrawPoint"></t-button>
      <t-button
        v-if="!(point_index < geo_type)"
        theme="light"
        size="large"
        :icon="checkIcon"
        shape="circle"
        aria-label="完成"
        class="draw-point-save"
        @click="onDrawFinish"></t-button>
    </div>

    <div class="self-location" @click="getLocation">
      <img :src="locationSvg" alt="location" />
    </div>

    <div class="self-track" v-if="track_f_c_id">
      <t-button :theme="is_track_start ? 'danger' : 'default'" @click="trackMode">{{
        is_track_start ? "结束轨迹" : "开始轨迹"
      }}</t-button>
    </div>

    <!-- <div class="track-event" v-if="is_track_start">
      <t-button shape="round" @click="addTrackEvent">轨迹事件</t-button>
    </div> -->

    <div class="query-feature">
      <t-button @click="queryFeature">查询</t-button>
    </div>

    <div class="layer-container" @click="showLayers">
      <layers-icon size="2rem" style="color: rgb(32, 51, 160)"></layers-icon>
    </div>

    <t-popup v-model="layer_pop_up" placement="right">
      <div class="layer-container-title">图层列表</div>
      <t-notice-bar visible :prefix-icon="false" content="点击圆圈控制显隐" />
      <t-notice-bar visible :prefix-icon="false" content="点击名称开始添加要素" />
      <t-checkbox-group v-model="selected_f_c_ids" @change="onFCChange" class="layer-list">
        <template v-for="f_c in feature_class_list" :key="f_c.id">
          <t-checkbox :value="f_c.id" contentDisabled>
            <template #default>
              <div class="layer-name" @click="start(f_c)">{{ f_c.name }}</div>
            </template>
          </t-checkbox>
        </template>
      </t-checkbox-group>
    </t-popup>
    <t-popup v-model="properties_pop_up" placement="bottom" :closeOnOverlayClick="false" class="popup-t">
      <div class="header">
        <div class="btn btn--cancel" aria-role="button" @click="cancel">取消</div>
        <div class="title">要素属性</div>
        <div class="btn btn--confirm" aria-role="button" @click="submit">确定</div>
      </div>
      <t-form v-if="JSON.stringify(properties) !== '{}'" :data="properties" show-error-message label-align="left">
        <template v-for="(field, index) in f_c_fields" :key="index">
          <t-form-item :label="field.name">
            <t-input v-model="properties[field.name]" borderless></t-input>
          </t-form-item>
        </template>
        <t-form-item label="上传照片" name="files">
          <t-upload
            :default-files="files"
            multiple
            :requestMethod="uploadFile"
            :on-remove="onFileRemove"
            :on-preview="onFilePreview">
          </t-upload>
        </t-form-item>
        <t-form-item label="录音">
          <t-button :theme="is_start_record ? 'danger' : 'default'" @click="onStartRecord">{{
            is_start_record ? "停止" : "录制"
          }}</t-button>
        </t-form-item>
      </t-form>
    </t-popup>

    <t-popup v-model="feature_option_pop_up" placement="bottom" :closeOnOverlayClick="false" class="popup-t">
      <div class="row">
        <t-button theme="light" @click="cancel2">取消</t-button>
        <t-button theme="primary" @click="submit2">提交</t-button>
        <t-button theme="danger" @click="deleteFeature">删除</t-button>
      </div>
      <t-form v-if="JSON.stringify(properties) !== '{}'" :data="properties" show-error-message label-align="left">
        <template v-for="(field, index) in f_c_fields" :key="index">
          <t-form-item :label="field.name">
            <t-input v-model="properties[field.name]" borderless></t-input>
          </t-form-item>
        </template>
        <t-form-item label="上传照片" name="files">
          <t-upload
            :default-files="files"
            multiple
            :requestMethod="uploadFile"
            :on-remove="onFileRemove"
            :on-preview="onFilePreview">
          </t-upload>
        </t-form-item>
        <t-form-item label="录音">
          <t-button :theme="is_start_record ? 'danger' : 'default'" @click="onStartRecord">{{
            is_start_record ? "停止" : "录制"
          }}</t-button>
        </t-form-item>
      </t-form>
    </t-popup>

    <t-popup v-model="audio_pop_up" placement="center" :on-close="onAudioClose">
      <audio controls ref="audioRef"></audio>
    </t-popup>
  </div>
</template>

<script setup lang="ts">
import { Map, Point, MapMouseEvent, Marker } from "mapbox-gl";
import { h, ref, onMounted, onUnmounted } from "vue";
import { AddIcon, CheckIcon, LayersIcon } from "tdesign-icons-vue-next";
import locationSvg from "../assets/location.svg";
import MapboxDraw from "../mapbox-draw";
import { useRoute } from "vue-router";
import { v4 } from "uuid";
import { Toast } from "tdesign-mobile-vue";
import {
  uploadAttachment,
  createFeature,
  getOneFeature,
  updateFeature,
  removeFeature,
  getFeatureClassListByDataSetId,
  getExtentDataSet,
  wxUpload,
} from "@/services";
import wx from "weixin-js-sdk";
import { addLayers, distance } from "@/utils";

type anyObject = {
  [name: string | number]: any;
};

const is_paint_layer = ref<boolean>(false);

const addIcon = () => h(AddIcon, { size: "2rem" });
const checkIcon = () => h(CheckIcon, { size: "2rem" });

let map: Map | null = null;
let draw: MapboxDraw | null = null;
let geo_type: number;

const point_index = ref<number>(0);
let feature: any = null;
const properties = ref<anyObject>({});
const files = ref<Array<any>>([]);

const route = useRoute();

const dataset_id = route.params.id as string;

const draw_tool = ref<boolean>(false);
const layer_pop_up = ref<boolean>(false);
const properties_pop_up = ref<boolean>(false);
const feature_option_pop_up = ref<boolean>(false);
const is_track_start = ref<boolean>(false);

const track_f_c_id = ref<string>("");
let track_timer: number;
const marker_list: Array<Marker> = [];
let track_line: any;

const selected_f_c_ids = ref<Array<string>>([]);
const layers = ref<Array<any>>([]);
const f_c_fields = ref<Array<any>>([]);
const feature_class_list = ref<Array<any>>([]);

const is_start_record = ref<boolean>(false);
const audio_pop_up = ref<boolean>(false);
const audioRef = ref<HTMLAudioElement>();

function loadLayers(dataset_id: string) {
  if (dataset_id) {
    getFeatureClassListByDataSetId(dataset_id).then((res) => {
      if (res.data.success) {
        feature_class_list.value = res.data.data
          .filter((i: any) => i.type < 4)
          .sort((a: any, b: any) => a.order - b.order);
        selected_f_c_ids.value = feature_class_list.value.filter((f_c: any) => f_c.property.visible).map((i) => i.id);
        const track_f_c = feature_class_list.value.find((f_c) => f_c.name.includes("轨迹"));
        if (track_f_c) {
          track_f_c_id.value = track_f_c.id;
          track_line = {
            id: v4(),
            fId: track_f_c.id,
            dataSetId: dataset_id,
            properties: {},
            geometry: {
              type: "LineString",
              coordinates: [],
            },
          };
        }
      }
    });
  }
}

function addDraw(map: Map | null) {
  const _draw = new MapboxDraw({
    keybindings: false,
    userProperties: true,
    boxSelect: false,
    displayControlsDefault: false,
  });
  draw = _draw;
  map?.addControl(draw);
  map?.on("load", () => {
    disableClick();
  });
}

function disableClick() {
  draw?.ctx.events.removeMouseUp();
  draw?.ctx.events.removeMouseDown();
  draw?.ctx.events.removeMouseMove();
}

function enableTap() {
  if (draw?.ctx && draw.ctx.events) {
    draw.ctx.events.addTouchStart();
    draw.ctx.events.addTouchEnd();
  }
}

function disableTap() {
  if (draw?.ctx && draw.ctx.events) {
    draw.ctx.events.removeTouchStart();
    draw.ctx.events.removeTouchEnd();
  }
}

function onMapLoad(map: Map) {
  const _layers = addLayers(map, dataset_id, feature_class_list.value);
  if (_layers.length) {
    layers.value = _layers;
  }
}

function initMap(style: any) {
  getExtentDataSet(dataset_id).then((res) => {
    if (res.data.success) {
      if (res.data.data) {
        const geom = res.data.data;
        const coords = geom?.coordinates?.[0];
        if (coords) {
          const lng = (coords[1][0] + coords[3][0]) / 2;
          const lat = (coords[1][1] + coords[3][1]) / 2;
          map?.setCenter([lng, lat]);
        }
      } else {
        map?.setCenter([120.220747, 30.249685]);
      }
    }
  });
  map = new Map({
    container: "map",
    accessToken: "pk.eyJ1IjoibHVja2RvZyIsImEiOiJjbGdrNXZmNWkxY2NqM3BteWltcjYwaGl4In0.Xm_EO9lIxHAiCcpJx6FT1A",
    style: style,
    maxZoom: 18.1,
    zoom: 9.1,
    minZoom: 9,
    interactive: false,
    doubleClickZoom: false,
  });
  map.dragPan.enable();
  map.touchZoomRotate.enable({ around: "center" });
  map.touchZoomRotate.disableRotation();
  const ctrl_top_left_dom = document.querySelector("#map .mapboxgl-control-container .mapboxgl-ctrl-top-left");
  const ctrl_top_right_dom = document.querySelector("#map .mapboxgl-control-container .mapboxgl-ctrl-top-right");
  const ctrl_bottom_left_dom = document.querySelector("#map .mapboxgl-control-container .mapboxgl-ctrl-bottom-left");
  const ctrl_bottom_right_dom = document.querySelector("#map .mapboxgl-control-container .mapboxgl-ctrl-bottom-right");
  ctrl_bottom_left_dom?.childNodes.forEach((item) => {
    item.remove();
  });
  ctrl_bottom_right_dom?.childNodes.forEach((item) => {
    item.remove();
  });
}

function addMapEvent(map: Map | null) {
  map?.on("load", (e) => {
    onMapLoad(map);
  });
}

onMounted(() => {
  import("../../satellite-streets-v11.json").then((res) => {
    initMap(res.default);
    addMapEvent(map);
    addDraw(map);
  });
  loadLayers(dataset_id as string);
  //用户授权
  getUserRecordAllow();
});

onUnmounted(() => {
  map && map.remove();
  draw = null;
  map = null;
});

function onFCChange() {
  layers.value.map((layer) => {
    if (selected_f_c_ids.value.some((id) => id === layer.source_id)) {
      layer.visible = true;
      if (map?.getLayer(layer.id)) {
        map?.setLayoutProperty(layer.id, "visibility", "visible");
      }
    } else {
      layer.visible = false;
      if (map?.getLayer(layer.id)) {
        map?.setLayoutProperty(layer.id, "visibility", "none");
      }
    }
  });
}

function showLayers() {
  layer_pop_up.value = !layer_pop_up.value;
}

function start(f_c: any) {
  if (f_c.name.includes("涂鸦")) {
    is_paint_layer.value = true;
  } else {
    is_paint_layer.value = false;
  }
  const obj: anyObject = {};
  f_c.fields.forEach((field: any) => {
    if (field.type.toLocaleLowerCase() === "string") {
      obj[field.name] = "";
    }
    if (field.type.toLocaleLowerCase() === "number") {
      obj[field.name] = 0;
    }
  });
  f_c_fields.value = f_c.fields;
  properties.value = obj;
  geo_type = f_c.type;
  feature = {
    id: v4(),
    fId: f_c.id,
    dataSetId: dataset_id,
    properties: {},
    files: [],
  };
  layer_pop_up.value = false;
  properties_pop_up.value = true;
}

function cancel() {
  properties_pop_up.value = false;
  properties.value = {};
  files.value = [];
}

function submit() {
  feature.properties = JSON.parse(JSON.stringify(properties.value));
  cancel();
  startDraw();
}

function startDraw() {
  if (is_paint_layer.value) {
    enableTap();
    draw?.changeMode("pass_draw_paint", {
      callBack: onFeatureCreate,
      errorBack: (err: any) => {
        Toast("禁止点击,点击涂鸦后拖动屏幕完成涂鸦");
      },
    });
    return;
  }
  disableClick();
  point_index.value = 0;
  draw_tool.value = true;
  switch (geo_type) {
    case 1:
      draw?.changeMode("pass_draw_point", { callBack: onFeatureCreate });
      break;
    case 2:
      draw?.changeMode("pass_draw_line_string", { callBack: onFeatureCreate });
      break;
    case 3:
      draw?.changeMode("pass_draw_polygon", { callBack: onFeatureCreate });
      break;
  }
}

function onDrawPoint() {
  if (map) {
    enableTap();
    const canvas = map.getCanvas();
    const x = canvas.clientWidth / 2;
    const y = canvas.clientHeight / 2;
    const point = new Point(x, y);
    const event = new MouseEvent("click");
    const lngLat = map.getCenter();

    const e: MapMouseEvent = {
      type: "click",
      target: map,
      point,
      originalEvent: event,
      lngLat,
      preventDefault: event.preventDefault,
      defaultPrevented: false,
    };
    map.fire("touchstart", e);
    map.fire("touchend", e);
    disableTap();
    point_index.value += 1;
    //当为点时，已绘制完成需要退出
    if (geo_type === 1) {
      draw_tool.value = false;
      point_index.value = 0;
      draw?.changeMode("simple_select");
    }
  }
}

function onFeatureCreate(f: any) {
  feature.geometry = f.geometry;
  feature.properties.id = feature.id;
  createFeature(feature).then((res) => {
    if (res.data.success) {
      refreshTile(feature.fId);
    }
  });
  disableTap();
}

function onDrawFinish() {
  draw_tool.value = false;
  point_index.value = 0;
  draw?.changeMode("simple_select");
}

function refreshTile(f_c_id: string) {
  if (map) {
    const source: any = map.getSource(f_c_id);
    if (source) {
      source.reload();
    }
  }
}

function queryFeature() {
  if (map) {
    const canvas = map.getCanvas();
    const x = canvas.clientWidth / 2;
    const y = canvas.clientHeight / 2;
    const features = map.queryRenderedFeatures(
      [
        [x - 8, y - 8],
        [x + 8, y + 8],
      ],
      { layers: layers.value.map((i) => i.id) }
    );
    if (features[0]?.id) {
      getOneFeature(features[0].id as string).then((res) => {
        if (res.data.success) {
          feature = res.data.data;
          const f_c = feature_class_list.value.find((i) => i.id === feature.fId);
          if (f_c) {
            const obj: anyObject = {};
            f_c.fields.forEach((field: any) => {
              obj[field.name] = feature.properties[field.name] || "";
            });
            f_c_fields.value = f_c.fields;
            properties.value = obj;

            if (feature.files?.length) {
              files.value = feature.files.map((file: any, index: number) => {
                return {
                  index,
                  id: file.id,
                  type: file.file_type,
                  name: file.original_name,
                  url: `${file.relative_path}/${file.auto_name}`,
                };
              });
            }
          } else {
            f_c_fields.value = [];
          }

          feature_option_pop_up.value = true;
        }
      });
    }
  }
}

function cancel2() {
  feature_option_pop_up.value = false;
  properties.value = {};
}

function submit2() {
  feature.properties = JSON.parse(JSON.stringify(properties.value));
  updateFeature(feature).then((res) => {
    if (res.data.success) {
      refreshTile(feature.fId);
    }
  });
  cancel2();
}

function deleteFeature() {
  if (feature) {
    removeFeature(feature.id).then((res) => {
      if (res.data.success) {
        refreshTile(feature.fId);
      }
    });
  }
  cancel2();
}

async function uploadFile(e: any) {
  const form = new FormData();
  form.append("files", e.fileRaw, e.name);

  const res = await uploadAttachment(form);
  if (res.data.success) {
    res.data.data.id = v4();
    feature.files = feature.files.concat(res.data.data);
    return { status: "success", response: e };
  } else {
    return { status: "fail", response: e };
  }
}

function onFileRemove(e: any) {
  if (feature) {
    const index = feature.files.findIndex((file: any) => file.id === e.file.id);
    index !== -1 && feature.files.splice(index, 1);
  }
}

function getLocation() {
  wx.ready(() => {
    wx.getLocation({
      type: "wgs84",
      success: (res) => {
        const { latitude, longitude } = res;
        const marker = new Marker();
        if (longitude && latitude && map) {
          marker.setLngLat([longitude, latitude]).addTo(map);
          map.flyTo({
            center: [longitude, latitude],
          });
          setTimeout(() => {
            marker.remove();
          }, 1000);
        }
      },
    });
  });
}

function trackMode() {
  is_track_start.value = !is_track_start.value;
  if (is_track_start.value) {
    track_timer = setInterval(() => {
      wx.ready(() => {
        wx.getLocation({
          type: "wgs84",
          success: (res) => {
            const { latitude, longitude } = res;
            if (map) {
              const marker = new Marker();
              marker.setLngLat([longitude, latitude]).addTo(map);
              const last_marker = marker_list[marker_list.length - 1];
              const last_lng_lat = last_marker.getLngLat();
              if (distance([longitude, latitude], [last_lng_lat.lng, last_lng_lat.lat]) > 30) {
                marker_list.push(marker);
                map.easeTo({
                  center: [longitude, latitude],
                  duration: 4500,
                });
                if (marker_list.length > 1 && track_line) {
                  const coordinates: any = [];
                  marker_list.forEach((marker) => {
                    const lngLat = marker.getLngLat();
                    const coord = [lngLat.lng, lngLat.lat];
                    coordinates.push(coord);
                  });
                  track_line.geometry.coordinates = coordinates;
                  if (marker_list.length === 2) {
                    createFeature(track_line).then((res) => {
                      if (res.data.success) {
                        refreshTile(track_line.fId);
                      }
                    });
                  } else {
                    updateFeature(track_line).then((res) => {
                      if (res.data.success) {
                        refreshTile(track_line.fId);
                      }
                    });
                  }
                }
              }
            }
          },
        });
      });
    }, 5000);
    // 持续获取点
  } else {
    clearInterval(track_timer);
    marker_list.forEach((marker) => {
      marker.remove();
    });
  }
}

function addTrackEvent() {
  // 获取定位
  // 添加事件
}

function startRecord() {
  wx.ready(() => {
    wx.startRecord();
    wx.onVoiceRecordEnd({
      complete: (res) => {
        if (res.localId) {
          uploadVoice(res.localId);
        }
      },
    });
  });
}

function stopRecord() {
  wx.ready(() => {
    wx.stopRecord({
      success: (res) => {
        if (res.localId) {
          is_start_record.value = false;
          uploadVoice(res.localId);
        }
      },
    });
  });
}

function uploadVoice(localId: string) {
  wx.uploadVoice({
    localId,
    success: (res) => {
      if (res.serverId) {
        wxUpload(res.serverId).then((res2) => {
          if (res2.data.success) {
            res2.data.data.id = v4();
            files.value.push({
              index: files.value.length,
              id: res2.data.data.id,
              name: res2.data.data.original_name,
              type: res2.data.data.file_type,
              url: res2.data.data.relative_path + res2.data.data.auto_name,
            });
            feature.files = feature.files.concat(res2.data.data);
          } else {
            Toast("录音上传失败");
          }
        });
      }
    },
  });
}

function getUserRecordAllow() {
  if (window.localStorage.getItem("isAllowRecord") !== "1") {
    wx.ready(() => {
      wx.startRecord({
        success: () => {
          window.localStorage.setItem("isAllowRecord", "1");
          wx.stopRecord({
            success: () => {},
          });
        },
        cancel: () => {
          Toast("拒绝授权将导致录音功能不可用!");
          window.localStorage.setItem("isAllowRecord", "0");
        },
      });
    });
  }
}

function onStartRecord() {
  if (window.localStorage.getItem("isAllowRecord") === "1") {
    is_start_record.value = !is_start_record.value;
    if (is_start_record.value) {
      startRecord();
    } else {
      stopRecord();
    }
  } else {
    // 用户授权
    getUserRecordAllow();
  }
}

//录音弹框关闭时
function onAudioClose() {
  audio_pop_up.value = false;
}

function onFilePreview(e: any) {
  if (e.file.type === "audio" && audioRef.value) {
    audio_pop_up.value = true;
    audioRef.value.src = e.file.url;
  }
}
</script>

<style scoped lang="scss">
.map-container {
  position: relative;
  height: 100%;
  width: 100%;
}

#map {
  height: 100%;
  width: 100%;
  z-index: 1;
}

.center-cursor {
  position: absolute;
  left: calc(50% - 1rem);
  top: calc(50% - 1rem);
  z-index: 2;
}

.draw-point-container {
  position: absolute;
  z-index: 2;
  left: calc(50% - 4rem);
  bottom: 2rem;

  .draw-point-add {
    margin-right: 2rem;
  }

  .draw-point-save {
    background: green;
  }

  :deep(.t-button::after) {
    display: none;
  }
}

.track-event {
  position: absolute;
  z-index: 2;
  left: calc(50% - 3rem);
  bottom: 2rem;
}

.self-track {
  position: absolute;
  z-index: 2;
  right: 1rem;
  bottom: 9rem;
}

.query-feature {
  position: absolute;
  z-index: 2;
  right: 1rem;
  bottom: 1rem;
}

.layer-container {
  position: absolute;
  z-index: 2;
  top: 10%;
  right: 5%;
}

.self-location {
  position: absolute;
  z-index: 2;
  bottom: 1rem;
  left: 1rem;
  img {
    height: 2rem;
    width: 2rem;
  }
}

.layer-list {
  max-height: calc(100vh - 8rem);
  overflow: auto;
}
.layer-name {
  margin-left: 1rem;
}

.layer-container-title {
  margin-left: 1rem;
}

.header {
  display: flex;
  align-items: center;
  height: 116rpx;
}

.title {
  flex: 1;
  text-align: center;
  font-weight: 600;
  font-size: 18px;
}

.btn {
  font-size: 16px;
  padding: 16px;
}

.btn--cancel {
  color: rgba(0, 0, 0, 0.6);
}

.btn--confirm {
  color: #0052d9;
}

.row {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: auto;
  justify-content: space-around;
  align-items: center;
}

.popup-t {
  padding-bottom: 1rem;
}
</style>
